/*
* Copyright (c) 2008, German Buela
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * The name of German Buela may not be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY GERMAN BUELA "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL GERMAN BUELA BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package org.gbuela.avant.util;

/**
 * Used for mapping command classes to and from their string representation
 * based on Avant's conventions and, if any, configuration.
 * @author gbuela
 */
public class CommandClassTranslator {
    public static final String COMMAND_SUFFIX = ".do";
    public static final String CLASSNAME_SUFFIX = "Command";
    private String packageName = null;
    private String commandSuffix = COMMAND_SUFFIX;
    
    /** Creates a new instance of CommandClassTranslator */
    public CommandClassTranslator() 
    {
    }
    
    public CommandClassTranslator(String pkgName, String suffix)
    {
        this.setPackageName(pkgName);
        this.setCommandSuffix(suffix);
    }

    public String getPackageName() 
    {
        return packageName;
    }
    public void setPackageName(String packageName) 
    {
        this.packageName = packageName;
    }
    public String getCommandSuffix()
    {
        return commandSuffix;
    }
    public void setCommandSuffix(String suffix)
    {
        this.commandSuffix = suffix;
    }
   
    /**
     * Given a string command as per the request URL, returns the inferred command class based on Avant's
     * conventions, and configuration (if any). A class should exist in the classpath with the inferred
     * name and package, otherwise it throws ClassNotFoundException.
     */
    public Class toClass(String cmd) throws ClassNotFoundException
    {
        require(cmd, "Command argument: null");
        
        if(packageName == null)
        {
            throw new IllegalStateException("Package name not set");
        }
        
        if(cmd.startsWith("/"))
            cmd = cmd.substring(1);
        
        String[] parts = cmd.split("~");
        
        String commandName = parts[parts.length - 1];
        String wholePkgName = packageName;
        for(int i=0; i < parts.length - 1; i++)
        {
            if(parts[i].length() > 0)
            {
                wholePkgName += "." + parts[i];
            }
        }
        
        commandName = commandName.substring(0, commandName.toLowerCase().lastIndexOf(commandSuffix));
        if(commandName.length() > 1)
            commandName = commandName.substring(0,1).toUpperCase() + commandName.substring(1);
        else
            commandName = commandName.toUpperCase();
        
        commandName += CLASSNAME_SUFFIX;

        Class commandClass = null;
        try
        {
            commandClass = Class.forName(wholePkgName + "." + commandName);
        }
        catch(NoClassDefFoundError ncdf)
        {
            throw new ClassNotFoundException("NoClassDefFoundError");
        }
        return commandClass;
    }
    
    /**
     * Given a command class, returns the string representation of the command which
     * would map to that command if issued in a request.
     */
    public String toCommand(Class cmdClass)
    {
        require(cmdClass, "Class argument: null");
        
        String cmdName = cmdClass.getName();
        if(!(cmdName.endsWith(CLASSNAME_SUFFIX)))
        {
            throw new IllegalArgumentException("Command class [" + cmdClass.getName() + "]: Suffix " + CLASSNAME_SUFFIX + " expected");
        }
        cmdName = cmdName.substring(0, cmdName.length() - CLASSNAME_SUFFIX.length());
        
        String wholePkgName = "";
        int dotLoc = cmdName.lastIndexOf(".");
        if(dotLoc > -1 && cmdName.length() > dotLoc)
        {
            wholePkgName = cmdName.substring(0, dotLoc);
            cmdName = cmdName.substring(dotLoc + 1);
        }
        String subPkg = "";
        if(wholePkgName.length() > 0 && packageName != null && !wholePkgName.equals(packageName) && wholePkgName.startsWith(packageName))
        {
            subPkg = wholePkgName.substring(packageName.length() + 1).replace('.', '~') + "~";
        }
        if(cmdName.length() == 1)
            cmdName = cmdName.toLowerCase();
        else
            cmdName = cmdName.substring(0,1).toLowerCase() + cmdName.substring(1);
        
        return subPkg + cmdName + commandSuffix;
    }
    
    private void require(Object o, String msg)
    {
        if(o == null)
        {
            throw new IllegalArgumentException(msg);
        }
    }
}
